﻿#include "StdAfx.h"
#include "DPI.h"
#include "VersionHelpers.h"
namespace DuiLib {
	//96 DPI = 100% scaling
	//120 DPI = 125% scaling
	//144 DPI = 150% scaling
	//168 DPI = 175% scaling
	//192 DPI = 200% scaling

	typedef HRESULT (WINAPI *LPSetProcessDpiAwareness)(
		_In_ PROCESS_DPI_AWARENESS value
		);

	typedef HRESULT (WINAPI *LPGetProcessDpiAwareness)(
		_In_  HANDLE                hprocess,
		_Out_ PROCESS_DPI_AWARENESS *value
		);


	typedef HRESULT (WINAPI *LPGetDpiForMonitor)(
		_In_  HMONITOR         hmonitor,
		_In_  MONITOR_DPI_TYPE dpiType,
		_Out_ UINT             *dpiX,
		_Out_ UINT             *dpiY
		);


	CDPI::CDPI () {
		m_nScaleFactor = 0;
		m_nScaleFactorSDA = 0;
		m_Awareness = PROCESS_PER_MONITOR_DPI_AWARE;

		SetScale (96);

	}

	int CDPI::GetDPIOfMonitor (HMONITOR hMonitor) {
		UINT dpix = 96, dpiy = 96;
		if (IsWindows8Point1OrGreater ()) {
			HRESULT  hr = E_FAIL;
			HMODULE hModule = ::LoadLibrary (_T ("Shcore.dll"));
			if (hModule) {
				LPGetDpiForMonitor GetDpiForMonitor = (LPGetDpiForMonitor) GetProcAddress (hModule, "GetDpiForMonitor");
				if (GetDpiForMonitor && GetDpiForMonitor (hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy) != S_OK) {
					MessageBox (nullptr, _T ("GetDpiForMonitor failed"), _T ("Notification"), MB_OK);
					return 96;
				}
			}
		} else {
			HDC screen = GetDC (0);
			dpix = GetDeviceCaps (screen, LOGPIXELSX);
			ReleaseDC (0, screen);
		}
		return dpix;
	}

	int CDPI::GetDPIOfMonitorNearestToPoint (POINT pt) {
		HMONITOR hMonitor;
		hMonitor = MonitorFromPoint (pt, MONITOR_DEFAULTTONEAREST);
		return GetDPIOfMonitor (hMonitor);
	}

	int CDPI::GetMainMonitorDPI () {
		POINT    pt;
		// Get the DPI for the main monitor
		pt.x = 1;
		pt.y = 1;
		return GetDPIOfMonitorNearestToPoint (pt);
	}

	PROCESS_DPI_AWARENESS CDPI::GetDPIAwareness () {
		if (IsWindows8Point1OrGreater ()) {
			HMODULE hModule = ::LoadLibrary (_T ("Shcore.dll"));
			if (hModule) {
				LPGetProcessDpiAwareness GetProcessDpiAwareness = (LPGetProcessDpiAwareness) GetProcAddress (hModule, "GetProcessDpiAwareness");
				if (GetProcessDpiAwareness) {
					HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, false, GetCurrentProcessId ());
					if (GetProcessDpiAwareness (hProcess, &m_Awareness) == S_OK) {
					}
				}
			}
		}

		return m_Awareness;
	}

	BOOL CDPI::SetDPIAwareness (PROCESS_DPI_AWARENESS Awareness) {
		BOOL bRet = FALSE;
		if (IsWindows8Point1OrGreater ()) {
			HMODULE hModule = ::LoadLibrary (_T ("Shcore.dll"));
			if (hModule) {
				LPSetProcessDpiAwareness SetProcessDpiAwareness = (LPSetProcessDpiAwareness) GetProcAddress (hModule, "SetProcessDpiAwareness");
				if (SetProcessDpiAwareness && SetProcessDpiAwareness (Awareness) == S_OK) {
					m_Awareness = Awareness;
					bRet = TRUE;
				}
			}
		} else {
			m_Awareness = Awareness;
		}
		return bRet;
	}

	UINT DuiLib::CDPI::GetDPI () {
		if (m_Awareness == PROCESS_DPI_UNAWARE) {
			return 96;
		}

		if (m_Awareness == PROCESS_SYSTEM_DPI_AWARE) {
			return MulDiv (m_nScaleFactorSDA, 96, 100);
		}

		return MulDiv (m_nScaleFactor, 96, 100);
	}

	UINT CDPI::GetScale () {
		if (m_Awareness == PROCESS_DPI_UNAWARE) {
			return 100;
		}
		if (m_Awareness == PROCESS_SYSTEM_DPI_AWARE) {
			return m_nScaleFactorSDA;
		}
		return m_nScaleFactor;
	}


	void CDPI::SetScale (UINT uDPI) {
		m_nScaleFactor = MulDiv (uDPI, 100, 96);
		if (m_nScaleFactorSDA == 0) {
			m_nScaleFactorSDA = m_nScaleFactor;
		}
	}

	int  CDPI::Scale (int iValue) {
		if (m_Awareness == PROCESS_DPI_UNAWARE) {
			return iValue;
		}
		if (m_Awareness == PROCESS_SYSTEM_DPI_AWARE) {
			return MulDiv (iValue, m_nScaleFactorSDA, 100);
		}
		return MulDiv (iValue, m_nScaleFactor, 100);
	}

	int  CDPI::ScaleBack (int iValue) {

		if (m_Awareness == PROCESS_DPI_UNAWARE) {
			return iValue;
		}
		if (m_Awareness == PROCESS_SYSTEM_DPI_AWARE) {
			return MulDiv (iValue, 100, m_nScaleFactorSDA);
		}
		return MulDiv (iValue, 100, m_nScaleFactor);
	}

	RECT CDPI::Scale (RECT rcRect) {
		RECT rcScale = rcRect;
		int sw = Scale (rcRect.right - rcRect.left);
		int sh = Scale (rcRect.bottom - rcRect.top);
		rcScale.left = Scale (rcRect.left);
		rcScale.top = Scale (rcRect.top);
		rcScale.right = rcScale.left + sw;
		rcScale.bottom = rcScale.top + sh;
		return rcScale;
	}

	void CDPI::Scale (RECT *pRect) {
		int sw = Scale (pRect->right - pRect->left);
		int sh = Scale (pRect->bottom - pRect->top);
		pRect->left = Scale (pRect->left);
		pRect->top = Scale (pRect->top);
		pRect->right = pRect->left + sw;
		pRect->bottom = pRect->top + sh;
	}

	void CDPI::ScaleBack (RECT *pRect) {
		int sw = ScaleBack (pRect->right - pRect->left);
		int sh = ScaleBack (pRect->bottom - pRect->top);
		pRect->left = ScaleBack (pRect->left);
		pRect->top = ScaleBack (pRect->top);
		pRect->right = pRect->left + sw;
		pRect->bottom = pRect->top + sh;
	}

	void CDPI::Scale (POINT *pPoint) {
		pPoint->x = Scale (pPoint->x);
		pPoint->y = Scale (pPoint->y);
	}

	POINT CDPI::Scale (POINT ptPoint) {
		POINT ptScale = ptPoint;
		ptScale.x = Scale (ptPoint.x);
		ptScale.y = Scale (ptPoint.y);
		return ptScale;
	}

	void CDPI::Scale (SIZE *pSize) {
		pSize->cx = Scale (pSize->cx);
		pSize->cy = Scale (pSize->cy);
	}

	SIZE CDPI::Scale (SIZE szSize) {
		SIZE szScale = szSize;
		szScale.cx = Scale (szSize.cx);
		szScale.cy = Scale (szSize.cy);
		return szScale;
	}
}